﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using MyFramework.Common;
using MyFramework.Common.Interfaces;

namespace MyFramework.Core
{
    /// <summary>
    /// viewmodel base
    /// </summary>
    public abstract class ViewModelBase : Entity, IViewModel, IValidationProvider
    {
        #region Fields

        /// <summary>
        /// The index main view
        /// </summary>
        private int indexMainView;

        /// <summary>
        /// The views
        /// </summary>
        [NonSerialized]
        private IViewCollection views;

        /// <summary>
        /// The model
        /// </summary>
        private IModel model;

        /// <summary>
        /// Occurs when [model set].
        /// </summary>
        public event EventHandler ModelSet;

        /// <summary>
        /// Occurs when [view model ready].
        /// </summary>
        public event EventHandler ViewModelReady;

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="ViewModelBase"/> class.
        /// </summary>
        public ViewModelBase()
        {
            this.Views = new ViewCollection(this);
            //this.Views.AddMainView(this.GetMainView());
            if (this.GetType().GetCustomAttributes(typeof(ValidationPerformerAttribute), true).Any())
            {
                this.ValidationProvider = GetValidationProvider();
                this.ValidationProvider.PropertyChanged += new PropertyChangedEventHandler(ValidationProvider_PropertyChanged);
            }
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the views.
        /// </summary>
        /// <value>
        /// The views.
        /// </value>
        public IViewCollection Views
        {
            get { return this.views; }
            private set { if (this.views != value) { this.views = value; this.RaisePropertyChangedEvent(() => this.Views); } }
        }

        /// <summary>
        /// Gets or sets the model.
        /// </summary>
        /// <value>
        /// The model.
        /// </value>
        public IModel Model
        {
            get { return this.model; }
            set { if (this.model != value) { this.model = value; RaisePropertyChangedEvent(() => Model); } }
        }

        /// <summary>
        /// Gets or sets the validation provider.
        /// </summary>
        /// <value>
        /// The validation provider.
        /// </value>
        protected IValidationProvider ValidationProvider { get; set; }

        /// <summary>
        /// Gets an error message indicating what is wrong with this object.
        /// </summary>
        /// <returns>An error message indicating what is wrong with this object. The default is an empty string ("").</returns>
        public virtual string Error
        {
            get { return this.ValidationProvider != null ? this.ValidationProvider.Error : string.Empty; }
        }

        /// <summary>
        /// Gets the error message for the property with the given name.
        /// </summary>
        /// <returns>The error message for the property. The default is an empty string ("").</returns>
        public virtual string this[string columnName]
        {
            get
            {
                string result = null;
                if (this.ValidationProvider != null)
                {
                    result = this.ValidationProvider[columnName];
                }

                RaisePropertyChangedEvent(() => this.Error);
                return result;
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Gets the main view.
        /// </summary>
        /// <returns></returns>
        /// <value>
        /// The main view.
        ///   </value>
        public abstract IView GetMainView();

        /// <summary>
        /// Handles the PropertyChanged event of the ValidationProvider control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param>
        private void ValidationProvider_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            this.RaisePropertyChangedEvent(e.PropertyName);
        }

        /// <summary>
        /// Gets the validation provider.
        /// </summary>
        /// <returns></returns>
        protected virtual IValidationProvider GetValidationProvider()
        {
            return new ValidationProvider(this);
        }

        /// <summary>
        /// Gets the validations.
        /// </summary>
        /// <param name="property">The property.</param>
        /// <returns></returns>
        private ValidationAttribute[] GetValidations(PropertyInfo property)
        {
            return (ValidationAttribute[])property.GetCustomAttributes(typeof(ValidationAttribute), true);
        }

        /// <summary>
        /// Gets the value.
        /// </summary>
        /// <param name="propertyName">The property name.</param>
        /// <returns></returns>
        private object GetValue(string propertyName)
        {
            return this.GetType().GetProperty(propertyName).GetValue(this, null);
        }

        /// <summary>
        /// Determines whether the specified property has property.
        /// </summary>
        /// <param name="propertyName">The property name.</param>
        /// <returns>
        ///   <c>true</c> if the specified property has property; otherwise, <c>false</c>.
        /// </returns>
        private bool HasProperty(string propertyName)
        {
            return this.GetType().GetProperty(propertyName) != null;
        }

        /// <summary>
        /// Initializes the specified parameter.
        /// </summary>
        /// <param name="parameter">The parameter.</param>
        public virtual void Initialize(IDictionary<string, object> parameter)
        {
            if (parameter != null && parameter.Count > 0)
            {
                PropertyInfo[] propertyInfos = this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
                foreach (PropertyInfo propertyInfo in propertyInfos)
                {
                    if (parameter.ContainsKey(propertyInfo.Name))
                    {
                        Type type = this.GetType();
                        PropertyInfo property = type.GetProperty(propertyInfo.Name);
                        property.SetValue(this, parameter[propertyInfo.Name], null);

                    }

                }
            }

        }

        /// <summary>
        /// Called when [model set].
        /// </summary>
        protected virtual void OnModelSet()
        {
            if (this.ModelSet != null)
            {
                this.ModelSet(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Raises the view model ready.
        /// </summary>
        public void RaiseViewModelReady()
        {
            OnViewModelReady();
        }

        /// <summary>
        /// Called when [view model ready].
        /// </summary>
        protected virtual void OnViewModelReady()
        {
            if (this.ViewModelReady != null)
            {
                this.ViewModelReady(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Sets the index main view.
        /// </summary>
        /// <param name="index">The index.</param>
        public virtual void SetIndexMainView(int index = 0)
        {
            this.indexMainView = index;
        }

        #endregion


    }
}
